#!/usr/bin/env python
"""

    fasta_single_line_sequence.py
    [--log_file PATH]
    [--quiet]

"""

#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   options        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

import optparse
import sys, os
import os.path
import StringIO

# add self to search path for testing
if __name__ == '__main__':
    exe_path = os.path.split(os.path.abspath(sys.argv[0]))[0]
    python_modules_path = os.path.abspath(os.path.join(exe_path,"..", "python_modules"))
    sys.path.append(python_modules_path)
    module_name = os.path.split(sys.argv[0])[1]
    module_name = os.path.splitext(module_name)[0];
else:
    module_name = __name__



if __name__ == '__main__':
    parser = optparse.OptionParser(version="%prog 1.0", usage = "\n\n    %progs [options]")
    parser.add_option("-i", "--input_file", dest="input_file",
                      metavar="FILE", 
                      type="string",
                      help="name and path of input file. Defaults to stdin.")
    parser.add_option("-o", "--output_file", dest="output_file",
                      metavar="FILE", 
                      type="string",
                      help="name and path of output file. Defaults to stdout.")
    
    #
    #   general options: verbosity / logging
    # 
    parser.add_option("-v", "--verbose", dest = "verbose",
                      action="store_true", default=False,
                      help="Print progress to STDERR")
    parser.add_option("-L", "--log_file", dest="log_file",
                      metavar="FILE", 
                      type="string",
                      help="name and path of log file")
    parser.add_option("--skip_parameter_logging", dest="skip_parameter_logging",
                      action="store_true", default=False,
                      help="Do not print program parameters to log.")
    parser.add_option("--debug", dest="debug",
                        action="store_true", default=False,
                        help="Set default program parameters in debugging mode.")
    
    
    
    
    
    # get help string
    f =StringIO.StringIO()
    parser.print_help(f)
    helpstr = f.getvalue()
    (options, remaining_args) = parser.parse_args()
    
    if options.debug:
        options.verbose = True
        options.log_file = os.path.join("fasta_single_line_sequence.log")
    
    # mandatory options
    from options import check_mandatory_options
    mandatory_options = []
    check_mandatory_options (options, mandatory_options, helpstr)

#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   imports        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

import StringIO
import re
import operator

from collections import defaultdict



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Functions        


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

def fasta_one_line_per_sequence (input_file, outstream,
                                 line_cnt_estimate = 60000000, logger = None, 
                                verbose = False):

    """
    Convert fasta data to one line per sequence, removing all intervening new lines
    Header lines are left untouched.
    """

    dots_every     = int(line_cnt_estimate / 100.0) + 1
    dots_line_len  = 50
    cnt_dots       = 0

    cnt_lines = 0
    cnt_sequences = 0
    non_empty_sequence = False
    curr_fasta_header = None
    curr_fasta_header_line = None

    if verbose:
        sys.stderr.write("    [")

    for l in input_file:
        cnt_lines += 1

        # print dots
        if cnt_lines % dots_every == 0:
            if verbose:
                sys.stderr.write(".")
                cnt_dots += 1
                if cnt_dots % dots_line_len ==0:
                    sys.stderr.write("\n     ")

        #header lines
        if l[0] == ">":

            # 
            #   Print new line for previous sequence if there was previous sequence
            # 
            if non_empty_sequence:
                outstream.write("\n")

            # 
            #   Warn if first sequence was not preceded by header
            # 
            else:
                if curr_fasta_header and logger:
                    logger.warning("The fasta header on line # %d " % curr_fasta_header_line +
                                   "was not followed by sequence data [%s]" % curr_fasta_header)

            #
            #   save and print header
            # 
            curr_fasta_header = l.rstrip("\r\n")
            curr_fasta_header_line = cnt_lines
            outstream.write(curr_fasta_header)
            outstream.write("\n")
            cnt_sequences += 1
            non_empty_sequence = False
            continue
        #sequence lines
        sequence_line = l.rstrip("\r\n")
        if not non_empty_sequence:
            non_empty_sequence = non_empty_sequence or len(sequence_line)
            if not curr_fasta_header and non_empty_sequence:
                if logger:
                    logger.warning("The sequence on line # %d " % cnt_lines +
                                   "was not preceded by a header line.")
        outstream.write(l.rstrip("\r\n"))

    # print dots
    if verbose:
        sys.stderr.write("]\n")

    return cnt_lines, cnt_sequences

#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Logger


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

import unittest
import logging
from lg_program_logging import setup_std_logging

if __name__ == "__main__":
    from options import get_option_strings
    logger = logging.getLogger(module_name)
    setup_std_logging(logger, options.log_file, options.verbose)

    if not options.skip_parameter_logging:
        programme_name = os.path.split(sys.argv[0])[1]
        logger.info("%s %s" % (programme_name, " ".join(get_option_strings(parser, options))))



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Unit testing


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

import unittest
import cStringIO

class Test_fasta_one_line_per_sequence(unittest.TestCase):

    #       self.assertEqual(self.seq, range(10))
    #       self.assert_(element in self.seq)
    #       self.assertRaises(ValueError, random.sample, self.seq, 20)



    def setUp(self):
        """
            use data in unit_testing_data for unit testing
        """
        fasta_data = \
        """
        
        sequence_without_header
        >chr1
        taccccactcacacttatggatagatcaactaaacagaaaattaacaagg
        aaaaaaaaaaAAAAAAAAACATGTATCTATAAAGCTCACTAAAGCAAAGC
        ACAGTAAAATGAGGTATGGCAATAAAAGGCGTTAAATTTATCaaaaaaaa
        aaaaaaaaagtgctgaaaaattaaaaaaaaaaaaaagaaaaaaaTATAGA
        >chr2
        taccccactcacacttatggatagatcaactaaacagaaaattaacaagg
        aaaaaaaaaaAAAAAAAAACATGTATCTATAAAGCTCACTAAAGCAAAGC
        ACAGTAAAATGAGGTATGGCAATAAAAGGCGTTAAATTTATCaaaaaaaa
        GTCTTAGCTCTTTTTTTTAATTAGACATGATATTTTCTTTCATCACATTT
        TCA
        >contig_with_missing_sequence
        >contig_with_missing_sequence2
        
        >contig_with_spurious_new_lines
        taccccactcacacttatggatagatcaactaaacagaaaattaacaagg
        aaa
        ACAGTAAAATGAGGTATGGCAATAAAAGGCGTTAAATTTATCaaaaaaaa
        aaaaaaaaagtgctgaaaaattaaaaaaaaaaaaaagaaaaaaaTATAGA


        GAT
        CTTTTCATTTTTTCAAATTTAATATTGTGTTACTCAAAAATACCCTAAAG
        TTGATCTCTGTTTAAATCTAGAACACTTTTTGTTAAGTTTATGTCGATAT
        TCAATATGTAGGAGAGCTAGTGATATTTGTGTA"""
        fasta_data = fasta_data.replace("\n" + " " * 8,  "\n").split("\n")
        self.fasta_data = map(lambda x: x + "\n", fasta_data)

    def test_fasta_one_line_per_sequence(self):
        #
        #   set up log
        # 
        log_output_file = cStringIO.StringIO()
        loghandler = logging.StreamHandler(log_output_file)
        loghandler.setFormatter(logging.Formatter("%(message)s"))
        loghandler.setLevel(logging.DEBUG)
        logger.addHandler(loghandler)

        output_file = cStringIO.StringIO()
        (cnt_lines, cnt_sequences) = fasta_one_line_per_sequence(self.fasta_data, output_file, len(self.fasta_data), logger)
        logger.info("%d lines in original file" % cnt_lines)
        logger.info("%d sequences in new file"  % cnt_sequences)    
        logger.info(output_file.getvalue())
        print log_output_file.getvalue()


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Main logic


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

    
    
    
if __name__ == '__main__':
    if options.debug:
        sys.argv = sys.argv[:1]
        unittest.main()
        sys.exit(-1)
        
    if not options.input_file or options.input_file == "-":
        input_file = sys.stdin
        line_cnt_estimate = 60000000
    else:
        input_file = open(options.input_file)
        line_cnt_estimate = os.path.getsize(options.input_file) / 50
        
    if not options.output_file:
        output_file = sys.stdout
    else:
        output_file = open(options.output_file, "w")
    
        
    if options.input_file and options.input_file != "-":
        logger.info("Input file = [%s]" % options.input_file)
    else:
        logger.info("Reading from stdin")

    (cnt_lines, cnt_sequences) = fasta_one_line_per_sequence (  input_file, output_file, 
                                                                line_cnt_estimate,
                                                                logger, 
                                                                options.verbose, 
                                                                )
        
        
    logger.info("%d lines in original file" % cnt_lines)
    logger.info("%d sequences in new file"  % cnt_sequences)    
    
    
    

